﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets ;
using Agrement;
using System.Windows.Threading;
namespace SockLib
{
   public  class SocketEventPool
    {
       
       public delegate void RegSocketModify(System.Collections.Specialized.NotifyCollectionChangedEventArgs e);
       public RegSocketModify RegSocketUpdate;
        /// <summary>
        /// 已注册成功的GPRS设备
        /// </summary>
        public SocketModelCollection RegSocket = new SocketModelCollection();

        /// <summary>
        /// 连接未注册GPRS设备
        /// </summary>
        public SocketModelCollection NoRegSocket = new SocketModelCollection();

        /// <summary>
        /// SocketModel池中的数据
        /// </summary>
     public    Queue <SocketModel> SocketPool = new Queue<SocketModel>();


        /// <summary>
        /// <para>判断时间周期（每隔一个周期进行一次判断计数</para>
        /// <para>连接上未能在时间段内注册将被剔除</para>
        /// </summary>
        public int TimeOutperiod { get; set; } = 30;


        private int _TimeOutCount=180;  //默认180秒
        /// <summary>
        /// 已经注册但是指定时间内没有发送心跳包，或产生数据流量
        /// 超时清除设备
        /// </summary>
        public int TimeOutCount
        {
            get
            {
                return _TimeOutCount;
            }

            set
            {
                _TimeOutCount = value;
            }
        }
        /// <summary>
        /// 设备连接数量
        /// </summary>
        public int ConnectDeviceNum
        {
            get
            {
                return  MaxConnectDevice-SocketPool .Count ;
            }
        }
        //internal  int _MaxConnectDevice;
        /// <summary>
        /// 最大连接设备数量
        /// </summary>
        public int MaxConnectDevice { set; get; }

        public SocketEventPool()
        {

        }


        /// <summary>
        /// SocketModel的连接池，一个SocketModel代表一个设备
        /// </summary>
        /// <param name="CapaCity">buffer容量</param>
        public SocketEventPool(int CapaCity,SocketModel.CloseClient SC,RegSocketModify Update,Logs.WriteLog WriteLog)
        {
            TimeOut = new System.Timers.Timer(new TimeSpan(0,0, TimeOutperiod).TotalMilliseconds );   //30S检查一次设备是否在线
            MaxConnectDevice = CapaCity;
            SocketModel.TransferDatas TF = new SocketModel.TransferDatas(TransferData);
            SocketModel.Register SR = new SocketModel.Register(DeviceRegister);
            TimeOut.Elapsed += new System.Timers.ElapsedEventHandler(TimeOut_Elapsed);
            for (int i = 0; i < CapaCity; i++)
            {
                SocketModel SM = new SocketModel(10240 );
                SM.DelegateInit(SC, TF, SR, WriteLog);
                SocketPool.Enqueue(SM );
            }      
            TimeOut.Start();
            RegSocket.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(RegSocket_CollectionChanged);
            RegSocketUpdate = Update;
        }

        public void  DisPoseSocketPool()
        {
            TimeOut.Elapsed-= TimeOut_Elapsed;
            RegSocket.CollectionChanged -= RegSocket_CollectionChanged;
        }

        void RegSocket_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            RegSocketUpdate(e);
        }

  
   

        /// <summary>
        /// 取一个空闲设备来使用
        /// </summary>
        /// <returns></returns>
        public SocketModel Pop()
        {
            if (SocketPool.Count > 0)
            {
                return SocketPool.Dequeue();
            }

            else
                return null;
        }

        /// <summary>
        /// 放入一个已经使用完的设备
        /// </summary>
        /// <param name="item"></param>
        public void Push(SocketModel item)
        {
            SocketPool.Enqueue (item);
        }


    /// <summary>
    /// 关闭自己
    /// </summary>
    /// <param name="S">要关闭的对象</param>
    /// <param name="CloseReason">
    /// <para>踢除设备原因</para>
    /// <para>0:超时未注册</para>
    /// <para>1：已存在设备再注册</para>
    /// <para>2:登录设备但是没有注册</para>
    /// </param>
        public void CloseMySelf(SocketModel S,int CloseReason)
        {
            lock(LockThis )
            {
                bool IsFindCenet = false;
                try
                {
                    if (S.ReceiveSAEA.AcceptSocket.Connected)
                        S.ReceiveSAEA.AcceptSocket.Shutdown(SocketShutdown.Both);
                    else
                       S.CloseOne(S);
                    if(!IsFindCenet )
                        WriteLog(S,CloseReason);
                }
                catch (Exception ex)
                {
                    logexception.WriteLogToFile(ex);
                }
                finally
                {
                    S.ReceiveSAEA.AcceptSocket.Close();
                }
            }
        }

        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="i"></param>
        private void WriteLog(SocketModel S,int CloseReason)
        {
            switch (CloseReason)
            {
                case 0:
                    string print = string.Format("{0}--{2}秒内没有心跳{1}被踢除", DateTime.Now.ToString(), S.Uid, (TimeOutCount+ TimeOutperiod).ToString());
                    Console.WriteLine(print);
                    S.WriteLog(S.Uid, "", 1, print);
                    break;
                case 1:
                    print = string.Format("{0}--{1}该ID设备已在线，踢除原在线设备", DateTime.Now.ToString(), S.Uid);
                    Console.WriteLine(print);
                    S.WriteLog(S.Uid, "", 1, print);
                    break;
                case 2:
                    print = string.Format("{0}--{2}秒内该设备{1}没有注册成功,被踢除", DateTime.Now.ToString(), S.LoginIP.ToString(), (TimeOutperiod+ TimeOutperiod).ToString());
                    Console.WriteLine(print);
                    S.WriteLog(S.Uid, "", 1, print);
                    break;
            }
        }

        /// <summary>
        /// 数据转发
        /// </summary>
        /// <param name="SourceData"></param>
        public void  TransferData(SocketModel SourceData)
        {
            lock (LockThis)
            {
                IEnumerable<SocketModel> query;
                for(int i=0;i<SourceData.Sendinfos.Count;i++)// (Device.DeviceModel.SendMsg DDS in SourceData.Sendinfos)
                {
                    Device.DeviceModel.SendMsg DDS = SourceData.Sendinfos[i];
                    if (DDS.TargetUid == "-1")   //群发
                    {
                        query = from SocketModel sm in RegSocket
                                where sm.IsCenter == !SourceData.IsCenter && sm.DeviceGroup == SourceData.DeviceGroup  //组号相同，中心发节点，节点发中心
                                select sm;
                    }
                    else if(DDS.TargetUid=="-2")  //没有注册成功回复
                    {
                        query = from SocketModel sm in NoRegSocket 
                                where sm==SourceData 
                                select sm;
                    }
                    else if(DDS.DeviceOper== Converts.DeviceOper.Register )
                    {
                        query = from SocketModel sm in RegSocket
                                where sm.Uid == DDS.TargetUid  && sm.DeviceGroup == SourceData.DeviceGroup  //组号相同，并且UID相同
                                select sm;
                    }
                    else    //指定目标发
                    {

                        if (SourceData.IsCenter)   //是中心发过来的数据，指定UID节点转发
                        {
                            query = from SocketModel sm in RegSocket
                                    where sm.Uid == DDS.TargetUid  && sm.DeviceGroup == SourceData.DeviceGroup  //组号相同，并且UID相同
                                    select sm;
                        }
                        else     //是节点发过来的数据，指定同组号中心转发
                        {
                            query = from SocketModel sm in RegSocket
                                    where sm.IsCenter == true && sm.DeviceGroup == SourceData.DeviceGroup 
                                    select sm;
                        }
                    }
                    foreach (SocketModel sm in query)
                    {

                        Device.DeviceModel.SendMsg SendMsg = new Device.DeviceModel.SendMsg();
                        SendMsg.TargetUid = sm.Uid;
                        byte[] SendData;
                        SendMsg.SendBuffer = DDS.SendBuffer;
                        if (sm.Uid == "-1")  //未注册
                        {
                            SendData = SendMsg.SendBuffer;
                            SendDataAstnc(SendData, sm);
                        }
                        else   //已经注册
                        {
                            if (sm.IsCenter && DDS.DeviceOper== Converts.DeviceOper.Transfer  )   //该设备是节点
                            {
                                DDS.Params = SourceData;
                                Device.DeviceModel.SendMsg[] DS =new Device.DeviceModel.SendMsg[] { DDS };
                                if (!sm.RegDevice.NodeTransferRule(DDS.SendBuffer, DDS.TargetUid, ref  DS ))  //节点返回数据根据接口文件进行转换
                                    continue;
                                    foreach (Device.DeviceModel.SendMsg DeviceSend in DS)
                                {
                                    SendMsg.SendBuffer = DeviceSend.SendBuffer;
                                    SendMsg.SendBuffer = sm.RegDevice.SendDataConvert(SendMsg.SendBuffer);
                                    SendMsg.Params =  DDS.Params ;
                                    SendData = sm.RegDevice.DataPacket(SendMsg);
                                    SendDataAstnc(SendData, sm );
                                }                                  
                            }
                            else
                            {
                         
                                    SendMsg.SendBuffer = sm.RegDevice.SendDataConvert(SendMsg.SendBuffer);
                                SendMsg.Params = DDS.Params;
                                    SendData = sm.RegDevice.DataPacket(SendMsg);
                                    SendDataAstnc(SendData, sm);
                                
                            }
    
                        }
                    }
                }
            }
        }

        private void SendDataAstnc(byte[] SendData, SocketModel   sm)
        {
            if (SendData.Length == 0)
                return;
            byte[] Buffer = new byte[1024*1024];
            SocketAsyncEventArgs SAEA = sm.SendSAEA(Buffer);
            Array.Copy(SendData, 0, Buffer, 0, SendData.Length);
            SAEA.SetBuffer(0, SendData.Length);
            SocketModel.SendCompleteinfos SMSC = new SocketModel.SendCompleteinfos();
            SMSC.senddata = SendData;
            SMSC.CD = Converts.DeviceOper.Transfer;
            SAEA.UserToken = SMSC;
            try
            {
                SAEA.AcceptSocket.SendAsync(SAEA);
            }
            catch (Exception ex)
            {
                logexception.WriteLogToFile(ex);
            }

            System.Threading.Thread.Sleep(1);
        }

        Agrement.Send_Agreement AgreMent = new Send_Agreement();
        System.Timers.Timer TimeOut;
        void TimeOut_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            HeartHandle();
        }


        /// <summary>
        /// 心跳维持处理，当指定时间没有心跳包则删除连接
        /// </summary>
        void HeartHandle()
        {
            try
            {
                lock (LockThis)
                {
                    for (int i = NoRegSocket.Count - 1; i >=0; i--)   //未注册的给30S进行注册
                    {
                        double Ts = (DateTime.Now - NoRegSocket[i].LoginTime).TotalSeconds;
                        if (Ts >= TimeOutperiod)    //登陆后至少给TimeOutperiod秒进行注册信息发送，否则踢除
                            CloseMySelf(NoRegSocket[i],2);
                    }
                    for (int i = RegSocket .Count - 1; i >= 0; i--)    //成功注册的给180秒进行发心跳包
                    {
                        double Ts = (DateTime.Now - RegSocket[i].RegTime).TotalSeconds;
                        if (Ts >= TimeOutCount)    //如果TimeOutCount分钟没有心跳包或数据则认为掉线，需要清除列表
                        {
                            CloseMySelf(RegSocket[i], 0);   //发送注销指令
                            continue;
                        }
                    }
                    RegCmd();
                }
            }
            catch (Exception ex)
            {
                logexception.WriteLogToFile(ex);
            }
        }



        ///// <summary>
        ///// 在线设备统计
        ///// </summary>
        //private void RegCmd()
        //{
        //    lock (LockThis)
        //    {
        //        var query = from SocketModel SM in RegSocket                 //获取所有中心模块
        //                    where SM.IsCenter == true
        //                    select SM;  
        //        foreach (SocketModel S in query )
        //        {
        //            var SocketGroup = from SocketModel SocketM in RegSocket     //获取和中心模块同组的节点模块
        //                              where SocketM.DeviceGroup == S.DeviceGroup
        //                              select SocketM;
        //            List<string> RegCode = new List<string>();
        //            foreach (SocketModel M in SocketGroup )
        //            {
        //                double Ts = (DateTime.Now - M.RegTime).TotalSeconds;
        //                if (Ts >= TimeOutCount)    //如果3分钟没有心跳包或数据则认为掉线，需要清除列表
        //                {
        //                    CloseMySelf(M, 0);   //发送注销指令
        //                    continue;
        //                }
        //                RegCode.Add(M.Uid);
        //            }
        //            if (RegCode.Count > 0)
        //            {
        //                List<byte[]> DeviceSendbyte = S.RegDevice.HeartBeatConvert(S.Uid, RegCode.ToArray());
        //                if (DeviceSendbyte == null)
        //                    continue ;
        //                foreach (byte[] DeviceSend in DeviceSendbyte)
        //                {
        //                    Device.DeviceModel.SendMsg SendMsg = new Device.DeviceModel.SendMsg();
        //                    SendMsg.SendBuffer = S.RegDevice.SendDataConvert(DeviceSend);
        //                    SendMsg.TargetUid = S.Uid;
        //                    byte[] Send = S.RegDevice.DataPacket(SendMsg);
        //                    byte[] Buffer = new byte[1024*1024];
        //                    SocketAsyncEventArgs SAEA = S.SendSAEA(Buffer);
        //                    Array.Copy(Send, Buffer, Send.Length);
        //                    SAEA.SetBuffer(0, Send.Length);
        //                    SocketModel.SendCompleteinfos SMSC = new SocketModel.SendCompleteinfos();
        //                    SMSC.senddata = Send;
        //                    SMSC.CD = Converts.DeviceOper.Register;
        //                    SAEA.UserToken = SMSC;
        //                    try
        //                    {
        //                        SAEA.AcceptSocket.SendAsync(SAEA);  //异步发送
        //                    }
        //                    catch (Exception ex)
        //                    {
        //                        logexception.WriteLogToFile(ex);
        //                        continue;
        //                    }
        //                    System.Threading.Thread.Sleep(1);
        //                }
        //            }

        //        }
        //    }
        //}


        /// <summary>
        /// 在线设备统计
        /// </summary>
        private void RegCmd()
        {
            lock (LockThis)
            {
                var query = from SocketModel SM in RegSocket                 //获取所有中心模块
                            where SM.IsCenter == true
                            select SM;
                foreach (SocketModel S in query)
                {
                    var SocketGroup = from SocketModel SocketM in RegSocket     //获取和中心模块同组的节点模块,发送在线统计
                                      where SocketM.DeviceGroup == S.DeviceGroup
                                      select SocketM;
                    List<string> RegCode = new List<string>();
                    foreach (SocketModel M in SocketGroup)
                        RegCode.Add(M.Uid);
                    if (RegCode.Count > 0)
                    {
                        List<byte[]> DeviceSendbyte = S.RegDevice.HeartBeatConvert(S.Uid, RegCode.ToArray());
                        if (DeviceSendbyte == null)
                            continue;
                        foreach (byte[] DeviceSend in DeviceSendbyte)
                        {
                            Device.DeviceModel.SendMsg SendMsg = new Device.DeviceModel.SendMsg();
                            SendMsg.SendBuffer = S.RegDevice.SendDataConvert(DeviceSend);
                            SendMsg.TargetUid = S.Uid;
                            byte[] Send = S.RegDevice.DataPacket(SendMsg);
                            byte[] Buffer = new byte[1024 * 1024];
                            SocketAsyncEventArgs SAEA = S.SendSAEA(Buffer);
                            Array.Copy(Send, Buffer, Send.Length);
                            SAEA.SetBuffer(0, Send.Length);
                            SocketModel.SendCompleteinfos SMSC = new SocketModel.SendCompleteinfos();
                            SMSC.senddata = Send;
                            SMSC.CD = Converts.DeviceOper.Register;
                            SAEA.UserToken = SMSC;
                            try
                            {
                                SAEA.AcceptSocket.SendAsync(SAEA);  //异步发送
                            }
                            catch (Exception ex)
                            {
                                logexception.WriteLogToFile(ex);
                                continue;
                            }
                            System.Threading.Thread.Sleep(1);
                        }
                    }
                }
            }

        }


            
        

        /// <summary>
        /// 返回注销指令
        /// </summary>
        /// <param name="SocketM"></param>
        private void RegCmd(SocketModel SocketM)
        {
            lock (LockThis)
            {
                //发送注册指令,更新在线时间
                var query = from SocketModel SM in RegSocket
                            where SM.DeviceGroup == SocketM.DeviceGroup && SM.IsCenter == true  //注册信息发给同组的设备
                            select SM;
                foreach (SocketModel S in query)
                {
                    byte[] byteArray = System.Text.Encoding.Default.GetBytes(SocketM.Uid);
                    byte[] DeviceSend = AgreMent.Make_AgreementNode(0x0b, 0x01, new int[] { 0 }, byteArray);
                    Device.DeviceModel.SendMsg SendMsg = new Device.DeviceModel.SendMsg();
                    SendMsg.SendBuffer =S.RegDevice.SendDataConvert( DeviceSend);
                    SendMsg.TargetUid = S.Uid;
                    S.RegTime = DateTime.Now;
                    byte[] Send = S.RegDevice.DataPacket(SendMsg);
                    byte[] Buffer = new byte[1024*1024];
                    SocketAsyncEventArgs SAEA = S.SendSAEA(Buffer);
                    Array.Copy(Send, Buffer, Send.Length);
                    SAEA.SetBuffer(0, Send.Length);
                    SocketModel.SendCompleteinfos SMSC = new SocketModel.SendCompleteinfos();
                    SMSC.senddata = Send;
                    SMSC.CD = Converts.DeviceOper.Register ;
                    SAEA.UserToken = SMSC;
                    try
                    {
                        SAEA.AcceptSocket.SendAsync(SAEA);  //异步发送
                    }
                    catch (Exception ex)
                    {
                        logexception.WriteLogToFile(ex);
                        continue;
                    }
                   
                    System.Threading.Thread.Sleep(1);
                }
            }
        }

        public  object LockThis=new object();
        /// <summary>
        /// 设备相同组号的按照顺序依次放在一起，设备注册规则：
        /// 1、设备已经在注册列表中的处理：不再进行任何处理，再次注册必须断线从来
        /// 2、设备不在注册列表中的处理
        /// 2.1、查看相同的UID是否被设备注册，如已经注册则替换
        /// 2.2、未被注册，搜索是否有相同组号设备，找到在相应放入设备
        /// 2.2、未找到相同组号，在注册列表末尾添加
        /// 3、移除未注册列表中的项
        /// </summary>
        /// <param name="S"></param>
        void  DeviceRegister(SocketModel S)
        {
            lock(LockThis)
            {
                if (RegSocket.Contains(S))  //如果已经注册了的模块再发注册则不进行任何操作
                    return;
                else
                {
                    bool IsFindDevice = false;  //是否找到了匹配设备
                    var query = from SocketModel SM in RegSocket
                                where SM.Uid == S.Uid
                                select SM;
                    foreach (SocketModel SM in query)
                    {   //注册的设备已经有了该ID，则新注册设备踢除原设备
                        int index = RegSocket.IndexOf(SM);   //获取设备位置
                        CloseMySelf(SM ,1);    //踢除设备
                        RegSocket.Remove(SM);  //列表中移除
                        RegSocket.Insert(index, S);  //新设备放入原位置
                        NoRegSocket.Remove(S);   //移除未注册列表中的设备
                        IsFindDevice = true;     //找到同号设备
                        break;                  //停止查找
                    }
                    if (!IsFindDevice)   //新注册的设备
                    {
                        IsFindDevice = false;   //是否找到匹配设备
                        var query1 = from SocketModel SM in RegSocket
                                     where SM.DeviceGroup == S.DeviceGroup
                                     select SM;
                        foreach (SocketModel SM in query1)
                        {
                            int index = RegSocket.IndexOf(SM);     //找到同组号的位置
                            RegSocket.Insert(index, S);           //指定位置插入设备
                            IsFindDevice = true;                             //找到匹配设备
                            break;                              //停止查找
                        }
                        if (!IsFindDevice )
                        {
                            RegSocket.Add(S);   //没有找到组号，在最后添加
                        }
                        NoRegSocket.Remove(S);   //移除未注册列表中的设备
                    }


                }
            }
        }

    }
}
